#include "FileUtils.h"

#include <string>
#include <iostream>
#include <fstream>

#include "Output/Console.h"
#include "Platform/PlatformInterface.h"

CString SFileUtils::ReadToString(const SPath & FilePath)
{
    std::ifstream File(FilePath.GetLowLevel());
    if (!File.is_open())
    {
        WH::PrintLine(U"Read file failed ["+ FilePath.ToString()+ U"]");
        assert(false);
        return CString();
    }

    std::u8string FileContent((std::istreambuf_iterator<char>(File)),
        (std::istreambuf_iterator<char>()));
    return CString(FileContent.c_str());

}

TVector<uint8_t> SFileUtils::ReadToBinary(const SPath &InFilePath,int64_t MaxSize)
{
    TVector<uint8_t> FileContent;
    {
        std::ifstream File(InFilePath.GetLowLevel(), std::ios::ate | std::ios::binary);
        if(!File.is_open())
        {
            //read vertext shader failed
            assert(false &&"Load file failed");
            return TVector<uint8_t>();
        }
        uint64_t FileSize = File.tellg();

        if(MaxSize>0)
        {
            FileSize = FileSize< MaxSize ?  FileSize : (uint64_t)MaxSize;
        }

        FileContent.Resize(FileSize/sizeof(uint8_t));
        File.seekg(0);
        File.read((char*)FileContent.Data(), FileSize);
    }
    return FileContent;
    
}

void SFileUtils::SaveTo(const CString & FileContent, const SPath & FilePath)
{
    std::ofstream File(FilePath.GetLowLevel(), std::ios::out);
    if (!File.is_open())
    {
        assert(false);
        return;
    }

    auto FileContent8 = FileContent.ToUtf8();
    File << (const char* )FileContent8.Data();

}

void SFileUtils::SaveBinaryTo(const TVector<uint8_t> &FileContent, const SPath &FilePath)
{
    std::ofstream File(FilePath.GetLowLevel(), std::ios::out | std::ios::binary);
    if (!File.is_open())
    {
        assert(false);
        return;
    }

    File.write((const char*)FileContent.Data(), FileContent.Num());

}

CString SFileUtils::RenameFileOrDirectory(const SPath &OldDirectory, const CString &NewDirectoryName)
{
    //rename directory
    std::filesystem::path OldDirectoryPath(OldDirectory.GetLowLevel());
    std::filesystem::path NewDirectoryPath = (OldDirectory.Parent()/NewDirectoryName).GetLowLevel();

    std::error_code  ErrorCode;
    std::filesystem::rename(OldDirectoryPath, NewDirectoryPath, ErrorCode);

    if (ErrorCode)
    {
        return IPlatformInterface::GetPlatformInterface()->SystemStringToUnicode(ErrorCode.message().c_str());
    }

    return CString();
}

CString SFileUtils::DeleteFile(const SPath & FilePath)
{
    std::filesystem::path FilePathLowLevel(FilePath.GetLowLevel());
    std::error_code  ErrorCode;
    std::filesystem::remove(FilePathLowLevel, ErrorCode);

    if (ErrorCode)
    {
        return IPlatformInterface::GetPlatformInterface()->SystemStringToUnicode(ErrorCode.message().c_str());
    }

    return CString();
}

CString SFileUtils::CreateDirectory(const SPath &DirectoryPath)
{
    std::filesystem::path DirectoryPathLowLevel(DirectoryPath.GetLowLevel());
    std::error_code  ErrorCode;
    std::filesystem::create_directory(DirectoryPathLowLevel, ErrorCode);

    if (ErrorCode)
    {
        return IPlatformInterface::GetPlatformInterface()->SystemStringToUnicode(ErrorCode.message().c_str());
    }

    return CString();
}

CString SFileUtils::DeleteDirectory(const SPath &DirectoryPath)
{
    std::filesystem::path DirectoryPathLowLevel(DirectoryPath.GetLowLevel());
    std::error_code  ErrorCode;
    std::filesystem::remove_all(DirectoryPathLowLevel, ErrorCode);

    if (ErrorCode)
    {
        return IPlatformInterface::GetPlatformInterface()->SystemStringToUnicode(ErrorCode.message().c_str());
    }

    return CString();
}

CString SFileUtils::ClearDirectory(const SPath &DirectoryPath)
{
    std::filesystem::path DirectoryPathLowLevel(DirectoryPath.GetLowLevel());
    std::error_code  ErrorCode;
    //移除目录下的所有文件和子目录，但不删除目录本身
    
    for (auto &Entry : std::filesystem::directory_iterator(DirectoryPathLowLevel))
    {
        std::filesystem::remove_all(Entry, ErrorCode);
        if (ErrorCode)
        {
            return IPlatformInterface::GetPlatformInterface()->SystemStringToUnicode(ErrorCode.message().c_str());
        }
    }

    return CString();

}

void SFileUtils::CopyDirectory(
    const SPath &SourceDirectory
    ,const SPath &TargetDirectory
    ,bool bRecursive
    ,bool bOverwrite 
    )
{
    std::filesystem::copy_options CopyOptions = std::filesystem::copy_options::none;
    if (bRecursive)
    {
        CopyOptions |= std::filesystem::copy_options::recursive;
    }

    if (bOverwrite)
    {
        CopyOptions |= std::filesystem::copy_options::overwrite_existing;
    }

    std::filesystem::copy(SourceDirectory.GetLowLevel(), TargetDirectory.GetLowLevel(), CopyOptions);
}

std::filesystem::file_time_type SFileUtils::GetLastWriteTime(const SPath &FilePath)
{
    std::filesystem::path FilePathLowLevel(FilePath.GetLowLevel());
    return std::filesystem::last_write_time(FilePathLowLevel);
}

bool SFileUtils::CopyFile(const SPath &Source, const SPath &Destination)
{
    std::filesystem::path SourceLowLevel(Source.GetLowLevel());
    std::filesystem::path DestinationLowLevel(Destination.GetLowLevel());
    std::error_code ErrorCode;
    std::filesystem::copy_file(SourceLowLevel, DestinationLowLevel, std::filesystem::copy_options::overwrite_existing, ErrorCode);
    return !ErrorCode;
}
